﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI.WebControls;
using System.IO;
using System.Drawing;

namespace Vit.Report.XmlWebReport
{
    public class Page
    {
        private SizeF pageSize;
        private SizeF contentSize;
        private SizeF cellSize;
        private Margin pageMargin;
        private PointF pageOffset;
        private PointF contentTopLeft;
        private RectanglePosition contentRectanglePosition;
        private RectanglePosition pageRectanglePosition;
        private Page extraXPage;
        private Page extraYPage;
        private Dictionary<string, RectangleF> cells;

        internal Page(Report rpt)
        {
            Elements = new List<CellElement>();
            cells = new Dictionary<string, RectangleF>();

            XOffset = 0;
            YOffset = 0;
            HeaderOffset = 0;
            FooterOffset = 0;
            Unit = "mm";
            PageIndex = 0;
            Report = rpt;
        }

        public Page(Report rpt, Page pageSetting)
        {
            this.PageSize = pageSetting.PageSize;
            this.PageMargin = pageSetting.PageMargin;
            this.PageOffset = pageSetting.PageOffset;
            this.BackGroundImageUrl = pageSetting.BackGroundImageUrl;
            this.ShowWaterMarkGrid = pageSetting.ShowWaterMarkGrid;
            this.PageRectanglePosition = pageSetting.PageRectanglePosition.Clone();
            this.DefaultFont = pageSetting.DefaultFont;

            Elements = new List<CellElement>();
            cells = new Dictionary<string, RectangleF>();

            XOffset = 0;
            YOffset = 0;
            HeaderOffset = 0;
            FooterOffset = 0;
            Unit = "mm";
            PageIndex = 0;
            Report = rpt;
        }

        public void CalculatePosition()
        {
            CalculateCells();
            if (string.IsNullOrEmpty(ID))
                ID = string.Format("page{0}", this.PageNo);
            
            foreach (CellElement ele in Elements)
                ele.CalculatePosition();

            if (this.extraXPage != null)
                this.extraXPage.CalculatePosition();
            if (this.extraYPage != null)
                this.extraYPage.CalculatePosition();
        }

        public void Render(TextWriter writer)
        {
            if (!string.IsNullOrEmpty(CssStyles))
            {
                writer.WriteLine(string.Format("<style type='text/css'>{0}</style>", CssStyles));
            }
            
            writer.WriteLine(@"<div id=""{0}"" class=""page"" style=""width:{2}{1};height:{3}{1};position:absolute;z-index:10;overflow:hidden;top:{4}{1};left:{5}{1};{6}"">",
                ID, Unit, PageSize.Width, PageSize.Height, PageOffset.Y, PageOffset.X, 
                string.IsNullOrEmpty(BackGroundImageUrl) ? "" : string.Format("background-image:url('{0}');", BackGroundImageUrl));

            if (ShowWaterMarkGrid)
            {
                //writer.WriteLine(@"<div id=""{0}-watermark"" class=""{1}"" style=""width:{3}{2};height:{4}{2};top:{5}{2};left:{6}{2};position:absolute;z-index:{7};"">",
                //    ID, "page-content", Unit, ContentSize.Width, ContentSize.Height, ContentTopLeft.Y, ContentTopLeft.X, 10);
                //writer.WriteLine("</div>");

                //for (int i = 1; i <= this.RectanglePosition.YSpan; i++)
                //{
                //    for (int j = 1; j <= this.RectanglePosition.XSpan; j++)
                //    {
                //        RectangleF rect = GetCellRectangle(string.Format("{0}{1}", (char)('A' + j - 1), i));

                //        writer.WriteLine(@"<div class=""watermark"" style=""width:{1}{0};height:{2}{0};line-height:{2}{0};top:{3}{0};left:{4}{0};position:absolute;z-index:{5};overflow:hidden;text-align:center;vertical-align:middle;"" title=""x:{4}{0}, y:{3}{0}"">{6}{7}</div>",
                //            Unit, rect.Width, rect.Height, rect.Y, rect.X, 11, (char)('A' + j - 1), i);
                //    }
                //}

                writer.WriteLine(@"<table id=""{0}-watermark"" class=""{1}"" border=""1"" cellpadding=""0"" cellspacing=""0"" style=""width:{3}{2};height:{4}{2};margin-top:{5}{2};margin-left:{6}{2};z-index:{7};"">",
                    ID, "page-content watermark", Unit, ContentSize.Width, ContentSize.Height, this.PageMargin.Top, this.PageMargin.Left, 20);

                for (int i = 1; i <= this.PageRectanglePosition.YSpan; i++)
                {
                    writer.WriteLine("<tr>");
                    for (int j = 1; j <= this.PageRectanglePosition.XSpan; j++)
                    {
                        RectangleF rect = GetCellRectangle(string.Format("{0}{1}", (char)('A' + j - 1), i));
                        writer.WriteLine(@"<td class=""watermark"" style=""text-align:center;vertical-align:middle;"" title=""x:{2}{0}, y:{1}{0}"">{3}{4}</td>",
                            Unit, rect.Y, rect.X, (char)('A' + j - 1), i);
                    }
                    writer.WriteLine("</tr>");
                }

                writer.WriteLine("</table>");
            }
            writer.WriteLine("</div>");

            foreach (CellElement ele in Elements)
                ele.Render(writer);

            this.Renderer.RenderPage(writer, this);

            if (this.extraXPage != null)
                this.extraXPage.Render(writer);
            if (this.extraYPage != null)
                this.extraYPage.Render(writer);

            if (!string.IsNullOrEmpty(Javascripts))
            {
                writer.WriteLine(string.Format("<script type='text/javascript'>$(function() {{ {0} }});</script>", Javascripts));
            }
        }

        public RectangleF GetCellRectangle(string cell)
        {
            if (cells.ContainsKey(cell))
                return cells[cell];
            return RectangleF.Empty;
        }

        private void CalculateCells()
        {
            cells.Clear();
            float x = this.ContentTopLeft.X, y = this.ContentTopLeft.Y;

            for (int i = 1; i <= this.PageRectanglePosition.YSpan; i++)
            {
                x = this.ContentTopLeft.X;
                for (int j = 1; j <= this.PageRectanglePosition.XSpan; j++)
                {
                    cells.Add(string.Format("{0}{1}",
                        (char)('A' + j - 1), i),
                        new RectangleF(x, y, this.CellSize.Width, this.CellSize.Height));

                    x += this.CellSize.Width;
                }
                y += this.CellSize.Height;
            }
        }

        public void Offset(int xOffset, int yOffset)
        {
            this.PageOffset = new PointF
            {
                X = this.pageOffset.X + xOffset * this.CellSize.Width,
                Y = this.pageOffset.Y + yOffset * this.CellSize.Height
            };

            this.ContentRectanglePosition.Move(xOffset, yOffset);

            foreach (CellElement ele in Elements)
            {
                ele.Move(xOffset, yOffset);
            }

            if (this.extraXPage != null)
                this.extraXPage.Offset(xOffset, yOffset);
            if (this.extraYPage != null)
                this.extraYPage.Offset(xOffset, yOffset);
        }

        public void Move(float x, float y)
        {
            this.PageOffset = new PointF
            {
                X = this.pageOffset.X + x,
                Y = this.pageOffset.Y + y
            };

            if (this.extraXPage != null)
                this.extraXPage.Move(x, y);
            if (this.extraYPage != null)
                this.extraYPage.Move(x, y);
        }

        public Page Clone()
        {
            Page page = new Page(this.Report);
            page.PageSize = this.PageSize;
            page.PageMargin = this.PageMargin;
            page.PageOffset = this.PageOffset;
            page.BackGroundImageUrl = this.BackGroundImageUrl;
            page.ShowWaterMarkGrid = this.ShowWaterMarkGrid;
            page.DefaultFont = this.DefaultFont;
            page.Unit = this.Unit;
            page.PageRectanglePosition = this.PageRectanglePosition.Clone();
            page.HeaderOffset = this.HeaderOffset;
            page.FooterOffset = this.FooterOffset;

            foreach (var ele in this.Elements.Where(o => o.IsHeader || o.IsFooter))
            {
                CellElement ce = new CellElement(page, ele.RectanglePosition, ele.Content);
                ce.CssClass = ele.CssClass;
                ce.CssStyle = ele.CssStyle;
                ce.Font = ele.Font;
                ce.Format = ele.Format;
                ce.LineHeight = ele.LineHeight;
                ce.LetterSpacing = ele.LetterSpacing;
                ce.NoWrap = ele.NoWrap;
                ce.PositionType = ele.PositionType;
                ce.IsFooter = ele.IsFooter;
                ce.IsHeader = ele.IsHeader;
                ce.IsHtml = ele.IsHtml;
            }

            page.Renderer = this.Renderer;

            return page;
        }

        public void EnsureExtraXPage()
        {
            if (extraXPage == null)
            {
                extraXPage = this.Clone();

                extraXPage.PageOffset = new PointF
                {
                    X = this.PageSize.Width + this.PageOffset.X,
                    Y = this.PageOffset.Y,
                };

                extraXPage.ID = string.Format("{0}_extraX", this.ID);
            }
        }
        public void EnsureExtraYPage()
        {
            if (extraYPage == null)
            {
                extraYPage = this.Clone();

                extraYPage.PageOffset = new PointF
                {
                    X = this.PageOffset.X,
                    Y = this.PageSize.Height + this.PageOffset.Y,
                };

                extraYPage.ID = string.Format("{0}_extraY", this.ID);
            }
        }

        public Page ExtraXPage
        {
            get
            {
                return extraXPage;
            }
        }

        public Page ExtraYPage
        {
            get
            {
                return extraYPage;
            }
        }

        public List<CellElement> Elements { get; set; }
        public string BackGroundImageUrl { get; set; }
        public int PageNo { get; set; }

        public SizeF PageSize
        {
            get { return pageSize; }
            set
            {
                pageSize = value;
            }
        }

        public Margin PageMargin
        {
            get { return pageMargin; }
            set
            {
                pageMargin = value;
            }
        }

        public PointF PageOffset
        {
            get { return pageOffset; }
            set
            {
                pageOffset = value;
            }
        }

        public PointF ContentTopLeft
        {
            get
            {
                if (contentTopLeft == PointF.Empty)
                {
                    contentTopLeft = new PointF()
                    {
                        X = PageMargin.Left + PageOffset.X,
                        Y = PageMargin.Top + PageOffset.Y
                    };
                }
                return contentTopLeft;
            }
        }

        public SizeF ContentSize
        {
            get
            {
                if (contentSize == SizeF.Empty)
                {
                    contentSize = new SizeF()
                    {
                        Width = ContentRectanglePosition.XSpan * CellSize.Width,
                        Height = ContentRectanglePosition.YSpan * CellSize.Height
                    };
                }
                return contentSize;
            }
        }

        public SizeF CellSize
        {
            get
            {
                if (cellSize == SizeF.Empty)
                {
                    cellSize = new SizeF()
                    {
                        Width = (PageSize.Width - PageMargin.Left - PageMargin.Right) / PageRectanglePosition.XSpan,
                        Height = (PageSize.Height - PageMargin.Top - PageMargin.Bottom) / PageRectanglePosition.YSpan
                    };
                }
                return cellSize;
            }
        }

        public string Unit { get; set; }

        public RectanglePosition PageRectanglePosition
        {
            get { return pageRectanglePosition; }
            set { pageRectanglePosition = value; }
        }

        public RectanglePosition ContentRectanglePosition
        {
            get
            {
                if (contentRectanglePosition == null)
                {
                    contentRectanglePosition = new RectanglePosition(
                        string.Format("{0}{1}", PageRectanglePosition.LeftIndex, PageRectanglePosition.TopIndex + HeaderOffset),
                        string.Format("{0}{1}", PageRectanglePosition.RightIndex, PageRectanglePosition.BottomIndex - FooterOffset));
                }
                return contentRectanglePosition;
            }
        }

        internal int XOffset { get; set; }
        internal int YOffset { get; set; }
        public int HeaderOffset { get; set; }

        public int FooterOffset { get; set; }

        public Font DefaultFont { get; set; }

        public bool ShowWaterMarkGrid { get; set; }
        public string ID { get; set; }
        public string Javascripts { get; set; }
        public string CssStyles { get; set; }

        public int PageIndex
        {
            get;
            set;
        }

        public Report Report
        {
            get;
            set;
        }

        public IReportRender Renderer
        {
            get;
            set; 
        }
    }
}
